﻿using MotorLibrary.DAL;
using MotorLibrary.Error;
using MotorLibrary.Model;
using MotorLibrary.Repository.Converter;
using MotorLibrary.Repository.DTO;
using System;
using System.Collections.Generic;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;

namespace MotorLibrary.Repository
{
    public abstract class GenericoRepository<EntityType, DTOType>: IRepository<EntityType, DTOType>
        where EntityType : class, IEntity
        where DTOType: IDTO
    {
        protected IConverter<EntityType, DTOType> _converter;
        protected IMotorDbContextFactory _factory;

        public GenericoRepository(IConverter<EntityType, DTOType> converter, IMotorDbContextFactory factory)
        {
            this._converter = converter;
            this._factory = factory;
        }

        public abstract DbSet<EntityType> GetDbSet(MotorDbContext context);

        public DTOType Read(long id)
        {
            DTOType resultado;
            EntityType entity;
            using (var context = _factory.GetInstance(false))
            {
                entity = GetDbSet(context).Find(id);
                resultado = _converter.EntityToDto(entity);
            }
            return resultado;
        }

        public IList<DTOType> List()
        {
            IList<DTOType> resultado = new List<DTOType>();

            IList<EntityType> entityList = new List<EntityType>();
            using (var context = _factory.GetInstance(false))
            {
                entityList = GetDbSet(context).ToList<EntityType>();
                context.SaveChanges();
                foreach (EntityType entity in entityList)
                {
                    resultado.Add(_converter.EntityToDto(entity));
                }
            }
            return resultado;
        }

        public DTOType Create(DTOType dto)
        {
            DTOType resultado;
            EntityType entity = _converter.DtoToEntity(dto);

            using (var context = _factory.GetInstance(false))
            {
                EntityType entityCreada = GetDbSet(context).Create();
                GetDbSet(context).Add(entityCreada);
                context.Entry(entityCreada).CurrentValues.SetValues(entity);

                context.SaveChanges();
                resultado = _converter.EntityToDto(entityCreada);
            }

            return resultado;
        }

        public DTOType Update(DTOType dto)
        {
            DTOType resultado;
            EntityType entity = _converter.DtoToEntity(dto);

            EntityType original;
            using (var context = _factory.GetInstance(false))
            {
                original = GetDbSet(context).Find(dto.ID);
                if (original == null)
                {
                    throw new NoEncontradaException(dto.ID, "No entidad no existe.");
                }
                context.Entry(original).CurrentValues.SetValues(entity);
                context.SaveChanges();
                resultado = _converter.EntityToDto(original);
            }

            return resultado;
        }

        public DTOType Delete(long id)
        {
            DTOType resultado;
            EntityType entity;
            using (var context = _factory.GetInstance(false))
            {
                entity = GetDbSet(context).Find(id);
                resultado = _converter.EntityToDto(entity);
                if (entity != null)
                {
                    entity = GetDbSet(context).Remove(entity);
                }
                context.SaveChanges();

            }

            return resultado;
        }

        public IList<DTOType> Filter(Expression<Func<EntityType, bool>> predicate)
        {
            IList<DTOType> resultado = new List<DTOType>();

            IList<EntityType> entityList = new List<EntityType>();
            using (var context = _factory.GetInstance(false))
            {
                entityList = GetDbSet(context).Where(predicate).ToList<EntityType>();
                foreach (EntityType entity in entityList)
                {
                    resultado.Add(_converter.EntityToDto(entity));
                }
            }
            return resultado;
        }
    }
}
